/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import { KEYBOARD_KEY } from '@app-cad/common/Constants';
// import { ctx2 } from 'nextcax-app'
// import { CadScene } from "nextcax-base"
// import { SceneGraph } from "nextcax-base"
// import { CommandHint, CommandHintStatus } from 'nextcax-components'
// import { useStore } from 'nextcax-base'
import { wsApi } from '@app-cad/server';
import i18n from 'i18next';
// import { CSys, MathTool } from "nextcax-math"
import { useEffect, useState } from 'react';
import { Object3D, Vector3 } from 'three-legacy';
// import { BaseCommand } from 'nextcax-app'
// import { CSLine, CSPoint, css2dInputLable } from "nextcax-bge"
// import { Picker } from "nextcax-picker"
import {
  BaseCommand,
  CadScene,
  CENTEREVENTS,
  CommandHint,
  CommandHintStatus,
  CSLine,
  CSPoint,
  css2dInputLable,
  CSys,
  EventsRes,
  MathTool,
  SceneGraph,
  useStore,
} from '@amcax/base';
import { Picker3D, PickerMgr } from '@amcax/renderer';
import { eventCenterManager } from '@app-base/common/events/EventCenterManager';
import { ctx2 } from '@app-cad/module';

// 草图 - 多段线
enum State {
  NONE,
  DRAW,
  FINISH,
}

type MouseState = State.NONE | State.DRAW | State.FINISH;

interface TrackingData {
  line: CSLine;
  label: css2dInputLable;
}

interface LineCommandProps {
  title: string;
  onClose: () => void;
}

function LineCommand({ onClose }: LineCommandProps) {
  const updateCmdHighlightId = useStore((state) => state.updateCmdHighlightId);
  const [hits, setHits] = useState<string>('');
  const cadScene = ctx2.cadScene as CadScene;
  const sceneSetup = ctx2.viewer.sceneSetup;
  const assisGroup = cadScene.toolsGroup;
  const xAxis = new Vector3(1, 0, 0);
  const yAxis = new Vector3(0, 1, 0);
  const zAxis = new Vector3(0, 0, 1);

  // 默认为 XOY 平面
  let faceCSys: CSys = MathTool.createCsysFromPlacement([
    [0, 0, 0],
    [0, 0, 1],
    [1, 0, 0],
  ]);
  let planeNormal: Vector3 = faceCSys.getNormalVc3();
  let planeOrigin: Vector3 = faceCSys.getOriginVc3();
  let planeName: string = 'XOY';
  let currentPlaneName: string = '';

  let startPoint: Vector3; // 绘制起点
  let endPoint: Vector3; // 绘制终点
  const points: Vector3[] = [];

  const assistPoints: CSPoint[] = [];

  let currentStatus: MouseState = State.NONE; // 记录当前状态

  const trackingData: TrackingData = {
    line: null,
    label: null,
  };

  useEffect(() => {
    // enter
    eventCenterManager.activeEventCenter.setState('draw').addExit(onDestroy);
    init();

    return onDestroy;
  }, []);

  const init = () => {
    registerEvents();
    setHits(
      '点击确定顶点，当前绘制平面为 ' +
        planeName +
        ' 平面，按方向键切换绘制平面',
    );
  };
  const onDestroy = () => {
    unregisterEvents();
    releaseObjects();
    onClose();
  };

  // ========= 鼠标和键盘事件=========
  const registerEvents = () => {
    eventCenterManager.activeEventCenter
      .setState('cmd-polyline')
      .on(CENTEREVENTS.POINTERUP, onMouseClick)
      .on(CENTEREVENTS.POINTERMOVE, onMouseMove)
      .on(CENTEREVENTS.KEYUP, onKeyup)
      .addExit(onDestroy);
  };
  const unregisterEvents = () => {
    eventCenterManager.activeEventCenter
      .off(CENTEREVENTS.POINTERUP, onMouseClick)
      .off(CENTEREVENTS.POINTERMOVE, onMouseMove)
      .off(CENTEREVENTS.KEYUP, onKeyup)
      .setDefault();
  };

  const onKeyup = (e: EventsRes<KeyboardEvent>) => {
    if (e.key === KEYBOARD_KEY.ESC) {
      onDestroy();
    } else if (e.key === KEYBOARD_KEY.ENTER) {
      if (currentStatus === State.DRAW) {
        finishDraw();
        onDestroy();
      }
    } else if (e.key === KEYBOARD_KEY.ARROWUP) {
      changePlaneToXOY();
    } else if (e.key === KEYBOARD_KEY.ARROWRIGHT) {
      changePlaneToYOZ();
    } else if (e.key === KEYBOARD_KEY.ARROWLEFT) {
      changePlaneToXOZ();
    } else if (e.key === KEYBOARD_KEY.ARROWDOWN) {
      changePlaneToXOY();
    }
  };
  const onMouseClick = (e: any) => {
    // if (currentStatus === State.NONE) {
    //   setPickMode(['sketch'])
    // }
    if (e.button.onLeft) {
      // trackingData.line.addTo(assisGroup)
      trackingData.line = null;
      // 鼠标左键，开始画线，记录起始点，并添加到points中
      if (points.length > 0) {
        // 如果已经画了线段，下个线段的起点设置为上个线段的终点
        startPoint = points[points.length - 1];
        planeOrigin = startPoint.clone();
      } else {
        startPoint = sceneSetup.mouseOnPlane(
          e.mouse.x,
          e.mouse.y,
          planeNormal,
          planeOrigin,
        );
        planeOrigin = startPoint.clone();
        // changePlaneToXOY()
      }
      points.push(startPoint);
      // 添加辅助点，用来做定点捕捉
      createAssistPoint(startPoint);

      currentStatus = State.DRAW;
    } else if (e.button.onRight) {
      currentStatus = State.FINISH;
      // 鼠标右键，完成
      finishDraw();
      onDestroy();
    }
  };
  const onMouseMove = (e: any) => {
    if (currentStatus === State.NONE) {
      const pickRes = PickerMgr.getPicker<Picker3D>('3d').pick(e, {
        face: true,
      });
      if (pickRes && pickRes?.assisObj?.csys) {
        planeNormal = pickRes.assisObj.csys?.getNormalVc3();
        planeOrigin = pickRes.assisObj.csys?.getOriginVc3();
        planeName = '拾取';
        if (planeName !== currentPlaneName) {
          setHits('点击确定顶点，当前绘制平面为 ' + planeName + ' 平面');
          currentPlaneName = planeName;
        }
      } else {
        if (planeName === '拾取') {
          planeOrigin = new Vector3(0, 0, 0);
          planeNormal = new Vector3(0, 0, 1);
          planeName = 'XOY';
          if (planeName !== currentPlaneName) {
            setHits(
              '点击确定顶点，当前绘制平面为 ' +
                planeName +
                ' 平面，按方向键切换绘制平面',
            );
            currentPlaneName = planeName;
          }
        }
      }
    }

    if (currentStatus === State.DRAW) {
      let hasPoint = false; // 是否有可吸附的点

      if (trackingData.line) {
        // let results = ctx2.viewer.raycast(e.event, assistPoints, RayCastDebugInfo)
        const raycaster = sceneSetup.createRaycaster(e.mouse.x, e.mouse.y);
        let results = raycaster.intersectObjects(assistPoints, true);
        // 过滤掉当前点，对当前点不做吸附判断
        results = results.filter((result) => {
          const ret = isLastPoint(result.object);
          return !ret;
        });
        if (results.length > 0) {
          const targetPoint = results[0].object as CSPoint;
          endPoint = targetPoint.getVertex();
          hasPoint = true;
        }
      }
      if (!hasPoint) {
        endPoint = sceneSetup.mouseOnPlane(
          e.mouse.x,
          e.mouse.y,
          planeNormal,
          planeOrigin,
        );
      }

      // 添加辅助点，用来做定点捕捉
      createAssistPoint(endPoint, true);

      if (points.length == 1) {
        points.push(endPoint);
      } else {
        points[points.length - 1] = endPoint;
      }

      preview();
    }
  };
  const createAssistPoint = (
    point: Vector3,
    is_move: boolean = false,
  ): CSPoint => {
    const assistPoint: CSPoint = new CSPoint({
      vertex: point,
      pointType: 'endPoint',
    });
    if (!is_move || assistPoints.length == 1) {
      assistPoints.push(assistPoint);
      assisGroup.add(assistPoint);
    } else {
      assisGroup.remove(assistPoints[assistPoints.length - 1]);
      assisGroup.add(assistPoint);
      assistPoints[assistPoints.length - 1] = assistPoint;
    }
    return assistPoint;
  };
  const isLastPoint = (point: Object3D): boolean => {
    if (points.length > 0) {
      const lastAssistPoint = assistPoints[assistPoints.length - 1];
      return lastAssistPoint.uuid == point.uuid;
    }
    return false;
  };
  // ========= 鼠标和键盘事件 =========
  // ========= 位置变换 =========
  const changePlaneToXOY = () => {
    changePlane(zAxis, 'XOY');
  };
  const changePlaneToYOZ = () => {
    changePlane(xAxis, 'YOZ');
  };
  const changePlaneToXOZ = () => {
    changePlane(yAxis, 'XOZ');
  };

  const changePlane = (_axis: Vector3, _planeName: string) => {
    planeName = _planeName;
    planeNormal = _axis.clone();
    createFaceCSys();
    preview();
    setHits(
      '点击确定顶点，当前绘制平面为 ' +
        planeName +
        ' 平面，按方向键切换绘制平面',
    );
  };

  const createFaceCSys = () => {
    let _xAxis = []; // 局部坐标系的X轴
    let _zAxis = []; // 局部坐标系的Z轴
    if (planeName === 'XOY') {
      _xAxis = [1, 0, 0];
      _zAxis = zAxis.clone().toArray();
    } else if (planeName === 'YOZ') {
      _xAxis = [0, 0, 1];
      _zAxis = xAxis.clone().toArray();
    } else if (planeName === 'XOZ') {
      _xAxis = [1, 0, 0];
      _zAxis = yAxis.clone().toArray();
    }
    const placement = [planeOrigin.clone().toArray(), _zAxis, _xAxis];
    faceCSys = MathTool.createCsysFromPlacement(placement);
    if (trackingData.line) {
      trackingData.line.resetCsys(faceCSys);
    }
  };

  // ========= 位置变换 =========

  const finishDraw = async () => {
    if (points.length > 0) {
      if (points[points.length - 2] === points[points.length - 1]) {
        points.pop();
      }
      await wsApi.createPolygon(points);
    }
  };

  // 释放对象
  const releaseObjects = () => {
    trackingData.line && trackingData.line.destroy();
    trackingData.label && trackingData.label.destroy();

    assisGroup.clear();
    SceneGraph.emptyGroup(assisGroup);
    ctx2.viewer.requestRender();

    updateCmdHighlightId('');
  };

  const preview = () => {
    if (currentStatus === State.DRAW) {
      if (!trackingData.line) {
        trackingData.line = new CSLine({
          vertexs: [startPoint, endPoint],
          dashed: false,
        });
        trackingData.line.addTo(assisGroup);
      } else {
        trackingData.line.update({ vertexs: [startPoint, endPoint] });
        // trackingData.line.setVertexs([startPoint, endPoint])
      }
      // draw input label
      const labelText = startPoint.distanceTo(endPoint);
      const labelPos = new Vector3();
      labelPos.x = (startPoint.x + endPoint.x) / 2;
      labelPos.y = (startPoint.y + endPoint.y) / 2;
      labelPos.z = (startPoint.z + endPoint.z) / 2;
      if (!trackingData.label) {
        trackingData.label = new css2dInputLable({
          text: labelText,
          position: labelPos,
        });
        trackingData.label.focus();
        trackingData.label.addTo(assisGroup);
      } else {
        trackingData.label.update(labelText, labelPos);
      }
      ctx2.viewer.requestRender();
    }
  };
  return (
    <CommandHint
      status={CommandHintStatus.INFO}
      content={hits}
    />
  );
}

export const cmdPolyline: BaseCommand = {
  id: 'CmdPolyline',
  label: 'command.polyline',
  icon: 'commands/feature/polyline',
  create: (label: string, onClose: () => void) => (
    <LineCommand
      title={i18n.t(label)}
      onClose={onClose}
    />
  ),
};
